package com.iflytek.autofly.setting.biz.networking;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.widget.Toast;

import com.iflytek.autofly.setting.R;
import com.iflytek.autofly.setting.util.MultiMap;
import com.iflytek.autofly.setting.util.SpUtil;
import com.iflytek.autofly.utils.FlyLog;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * <b>Description: </b>wifi设置<br/>
 * <b>Change History:</b><br/>
 * <table border="1">
 * <tr>
 * <td><b>user</b></td>
 * <td><b>date</b></td>
 * <td><b>time</b></td>
 * <td><b>reason</b></td>
 * </tr>
 * <tr>
 * <td>xjzhao@iflytek.com</td>
 * <td>2015年5月26日</td>
 * <td>上午11:35:55</td>
 * <td>创建此文件</td>
 * </tr>
 * </table>
 *
 * @author xjzhao@iflytek.com
 */
public class WlanSetting {
    public static final String WIFI_AP_STATE_CHANGED_ACTION =
            "android.net.wifi.WIFI_AP_STATE_CHANGED";
    private static final String TAG = "WlanSetting";
    private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
    private final AtomicBoolean mConnected = new AtomicBoolean(false);
    private Activity mActivity;
    private IWifiViewChangedListener mChanger = new IWifiViewChangedListener() {
        @Override
        public void onWifiStateChanged(int state) {
        }

        @Override
        public void setWifiListItems(ArrayList<AccessPoint> accessPoints) {
        }

        @Override
        public ArrayList<AccessPoint> getListItems() {
            return null;
        }

        @Override
        public void popPasswdWindow(AccessPoint bean, boolean needConnect, boolean modify) {
        }

        @Override
        public void updateConntecedWifiName(String name) {
        }

        @Override
        public void onApStateChanged(int state) {
        }

        @Override
        public void onApName(String name) {

        }

        @Override
        public void onApPasswd(String passwd) {

        }

        @Override
        public void onOutOfTraffic(int acticeStatus) {

        }

        @Override
        public void onApEnable() {
        }
    };
    private IntentFilter mIntentFilter;
    private FlyWifiManager mFlyWifiManager;
    private WifiManager mWifiManager;
    private Scanner mScanner;
    private DetailedState mLastState;
    private WifiInfo mLastInfo;
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            handleEvent(context, intent);
        }
    };
    private AtomicBoolean updateListEnabled = new AtomicBoolean(false);
    private volatile boolean mReady = false;

    public WlanSetting() {
    }

    public FlyWifiManager getManager() {
        return mFlyWifiManager;
    }

    public void init(Activity ctx, IWifiViewChangedListener viewChanger) {
        mActivity = ctx;
        if (viewChanger != null) {
            mChanger = viewChanger;
        }

        mWifiManager = (WifiManager) mActivity
                .getSystemService(Context.WIFI_SERVICE);
        mFlyWifiManager = FlyWifiManager.getInstance(mActivity);
        mScanner = new Scanner();

        if (mFlyWifiManager.isWifiEnabled()) {
            mChanger.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
            initWlanList();
        } else {
            mChanger.onWifiStateChanged(WifiManager.WIFI_STATE_DISABLED);
        }

        mChanger.onApStateChanged(mFlyWifiManager.getWifiApState());
        mChanger.onApName(loadApName());
        mChanger.onApPasswd(loadApPasswd());

        mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
        // The order matters! We really should not depend on this. :(
        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
        mIntentFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
        mIntentFilter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
        mReady = true;
    }

    public void initWlanList() {
        final ArrayList<AccessPoint> accessPoints = (ArrayList<AccessPoint>) constructAccessPoints();
        mChanger.setWifiListItems(accessPoints);
    }

    public void onResume() {
        if (mActivity != null) {
            mActivity.registerReceiver(mReceiver, mIntentFilter);
        }
    }

    public void onPause() {
        if (mActivity != null) {
            mActivity.unregisterReceiver(mReceiver);
        }
    }

    /**
     * Returns sorted list of access points
     *
     * @return List<AccessPoint>
     */
    private List<AccessPoint> constructAccessPoints() {
        ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
//        if (!updateListEnabled.get()) {
//            return accessPoints;
//        }
        /**
         * Lookup table to more quickly update AccessPoints by only considering
         * objects with the correct SSID. Maps SSID -> List of AccessPoints with
         * the given SSID.
         */
        MultiMap<String, AccessPoint> apMap = new MultiMap<String, AccessPoint>();

        final List<WifiConfiguration> configs = mWifiManager
                .getConfiguredNetworks();
        if (configs != null) {
            for (WifiConfiguration config : configs) {
                AccessPoint accessPoint = new AccessPoint(mActivity, config);
                accessPoint.update(mLastInfo, mLastState);
                apMap.put(accessPoint.ssid, accessPoint);
                accessPoints.add(accessPoint);
            }
        }

        final List<ScanResult> results = mWifiManager.getScanResults();
        if (results != null) {
            for (ScanResult result : results) {
                // Ignore hidden and ad-hoc networks.
                if (result.SSID == null || result.SSID.length() == 0
                        || result.capabilities.contains("[IBSS]")) {
                    continue;
                }

                boolean found = false;
                for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
                    if (accessPoint.update(result)) {
                        found = true;
                    }
                }
                if (!found) {
                    AccessPoint accessPoint = new AccessPoint(mActivity, result);
                    accessPoints.add(accessPoint);
                    apMap.put(accessPoint.ssid, accessPoint);
                }
            }
        }
        // Pre-sort accessPoints to speed preference insertion
        Collections.sort(accessPoints);
        return accessPoints;
    }

    private void handleEvent(Context context, Intent intent) {
        String action = intent.getAction();
        int mState;
        switch (action) {
            case WifiManager.WIFI_STATE_CHANGED_ACTION:
                mState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                        WifiManager.WIFI_STATE_UNKNOWN);
                if (mState != WifiManager.WIFI_STATE_ENABLING &&
                        mState != WifiManager.WIFI_STATE_DISABLING) {
                    mChanger.onWifiStateChanged(mState);
                }
                break;
            case WifiManager.SUPPLICANT_STATE_CHANGED_ACTION:
                if (!mConnected.get()) {
                    WifiInfo.getDetailedStateOf((SupplicantState) intent
                            .getParcelableExtra(WifiManager.EXTRA_NEW_STATE));
                }
                SupplicantState state = (SupplicantState) intent
                        .getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
                if (!mConnected.get() && isHandshakeState(state)) {
                    updateConnectionState(WifiInfo.getDetailedStateOf(state));
                } else {
                    // During a connect, we may have the supplicant
                    // state change affect the detailed network state.
                    // Make sure a lost connection is updated as well.
                    updateConnectionState(null);
                }
                break;
            case WifiManager.NETWORK_STATE_CHANGED_ACTION:
                NetworkInfo info = (NetworkInfo) intent
                        .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                if (ConnectivityManager.TYPE_WIFI == info.getType()) {
                    mConnected.set(info.isConnected());
                    updateConnectionState(info.getDetailedState());
                    updateAccessPoints();
                    if (mChanger != null) {
                        if (info.isConnected()) {
                            WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
                            if (wifiInfo != null) {
                                mChanger.updateConntecedWifiName(AccessPoint.removeDoubleQuotes(wifiInfo.getSSID()));
                            } else {
                                mChanger.updateConntecedWifiName(null);
                            }
                        } else {
                            mChanger.updateConntecedWifiName(null);
                        }
                    }
                }
                break;
            case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION:
                updateAccessPoints();
                break;
            case WifiManager.RSSI_CHANGED_ACTION:
                updateConnectionState(null);
                break;
            case WIFI_AP_STATE_CHANGED_ACTION:
                if (mChanger != null) {
                    mState = mFlyWifiManager.getWifiApState();
                    if (mState != FlyWifiManager.WIFI_AP_STATE_ENABLING &&
                            mState != FlyWifiManager.WIFI_AP_STATE_DISABLING) {
                        mChanger.onApStateChanged(mState);
                    }
                }
        }
    }

    private void updateConnectionState(DetailedState state) {
        /* sticky broadcasts can call this when wifi is disabled */
        if (!mWifiManager.isWifiEnabled()) {
            mScanner.pause();
            return;
        }

        if (state == DetailedState.OBTAINING_IPADDR) {
            mScanner.pause();
        } else {
            mScanner.resume();
        }

        mLastInfo = mWifiManager.getConnectionInfo();
        if (state != null) {
            mLastState = state;
        }
        @SuppressWarnings("unchecked")
        final ArrayList<AccessPoint> lists = (ArrayList<AccessPoint>) mChanger
                .getListItems();
        for (AccessPoint accessPoint : lists) {
            accessPoint.update(mLastInfo, mLastState);
        }
        Collections.sort(lists);

        // mFragment.notifyDataSetChanged();
    }

    private boolean isHandshakeState(SupplicantState state) {
        switch (state) {
            case AUTHENTICATING:
            case ASSOCIATING:
            case ASSOCIATED:
            case FOUR_WAY_HANDSHAKE:
            case GROUP_HANDSHAKE:
                return true;
            case COMPLETED:
            case DISCONNECTED:
            case INTERFACE_DISABLED:
            case INACTIVE:
            case SCANNING:
            case DORMANT:
            case UNINITIALIZED:
            case INVALID:
                return false;
            default:
                FlyLog.e(TAG, "Unknown supplicant state");
                return false;
        }
    }

    private void updateAccessPoints() {
        final int wifiState = mWifiManager.getWifiState();

        switch (wifiState) {
            case WifiManager.WIFI_STATE_ENABLED:
                //AccessPoints are automatically sorted with TreeSet.
                //final Collection<AccessPoint> accessPoints = constructAccessPoints();
                final ArrayList<AccessPoint> accessPoints = (ArrayList<AccessPoint>) constructAccessPoints();
                mChanger.setWifiListItems(accessPoints);
                break;

            case WifiManager.WIFI_STATE_ENABLING:
                break;

            case WifiManager.WIFI_STATE_DISABLING:
                mChanger.setWifiListItems(new ArrayList<AccessPoint>());
                break;

            case WifiManager.WIFI_STATE_DISABLED:
                mChanger.setWifiListItems(new ArrayList<AccessPoint>());
                break;
        }
    }

    public void cancelConnect(AccessPoint bean) {
        FlyLog.d(TAG, bean.ssid + ",id=" + bean.getNetWorkId());
        mWifiManager.disableNetwork(bean.getNetWorkId());
    }

    public void disconnect(AccessPoint bean) {
        FlyLog.d(TAG, bean.ssid + ",id=" + bean.getNetWorkId());
        mWifiManager.disableNetwork(bean.getNetWorkId());
    }

    public void connect(AccessPoint bean) {
        FlyLog.d(TAG, bean.ssid + ",id=" + bean.getNetWorkId());
        if (bean.getNetWorkId() == AccessPoint.INVALID_NETWORK_ID) {
            String passwd = null;
            if (bean.getSecurity() != AccessPoint.SECURITY_NONE) {
                mChanger.popPasswdWindow(bean, true, false);
                return;
            }

            WifiConfiguration config = newWifiConfiguration(bean, "");
            if (config.networkId == -1) {
                int id = mWifiManager.addNetwork(config);
                mWifiManager.saveConfiguration();
                mWifiManager.enableNetwork(id, true);
                return;
            } else {
                mWifiManager.updateNetwork(config);
                mWifiManager.saveConfiguration();
            }
        }
        mWifiManager.enableNetwork(bean.getConfig().networkId, true);
    }

    public void updateWithoutConnect(AccessPoint bean) {
        FlyLog.d(TAG, bean.ssid + ",id=" + bean.getNetWorkId());

        mChanger.popPasswdWindow(bean, false, true);
    }

    private WifiConfiguration newWifiConfiguration(AccessPoint accessPoint, String passwd) {

        WifiConfiguration config = new WifiConfiguration();

        if (accessPoint == null) {
            config.SSID = AccessPoint.convertToQuotedString(accessPoint.ssid);
            // If the user adds a network manually, assume that it is hidden.
            config.hiddenSSID = true;
        } else if (accessPoint.getNetWorkId() == AccessPoint.INVALID_NETWORK_ID) {
            config.SSID = AccessPoint.convertToQuotedString(accessPoint.ssid);
        } else {
            config.networkId = accessPoint.getNetWorkId();
        }
        switch (accessPoint.getSecurity()) {
            case AccessPoint.SECURITY_NONE:
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                break;

            case AccessPoint.SECURITY_WEP:
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
                // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
                int length = passwd.length();
                if ((length == 10 || length == 26 || length == 58) && passwd.matches("[0-9A-Fa-f]*")) {
                    config.wepKeys[0] = passwd;
                } else {
                    config.wepKeys[0] = '"' + passwd + '"';
                }

            case AccessPoint.SECURITY_PSK:
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
                if (passwd.length() != 0) {
                    if (passwd.matches("[0-9A-Fa-f]{64}")) {
                        config.preSharedKey = passwd;
                    } else {
                        config.preSharedKey = '"' + passwd + '"';
                    }
                }
                break;

            case AccessPoint.SECURITY_EAP:
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
                break;
            default:
                return null;
        }

        return config;
    }

    public void enableListUpdate(boolean b) {
        updateListEnabled.set(b);
    }

    public void saveConfig(AccessPoint ap, String result, boolean needConnect) {
        WifiConfiguration config = newWifiConfiguration(ap, result);
        int id = -1;
        if (config.networkId == -1) {
            id = mWifiManager.addNetwork(config);
            mWifiManager.saveConfiguration();
            ap.setConfig(config);
        } else {
            mWifiManager.updateNetwork(config);
            mWifiManager.saveConfiguration();
        }
        if (needConnect) {
            if (id != -1) {
                mWifiManager.enableNetwork(id, true);
            } else {
                mWifiManager.enableNetwork(config.networkId, true);
            }
        }
//        final ArrayList<AccessPoint> accessPoints = (ArrayList<AccessPoint>) constructAccessPoints();
//        mChanger.setWifiListItems(accessPoints);
    }

    public void modify(AccessPoint bean) {
        FlyLog.d(TAG, bean.ssid + ",id=" + bean.getNetWorkId());
        if (bean.getNetWorkId() != AccessPoint.INVALID_NETWORK_ID) {
            if (bean.getSecurity() != AccessPoint.SECURITY_NONE) {
                mChanger.popPasswdWindow(bean, false, true);
                return;
            }
        }
    }

    public void forget(AccessPoint bean) {
        FlyLog.d(TAG, bean.ssid + ",id=" + bean.getNetWorkId());
        mWifiManager.removeNetwork(bean.getNetWorkId());
        mWifiManager.saveConfiguration();
    }

    public boolean ready() {
        return mReady;
    }

    public boolean isWifiEnabled() {
        return mWifiManager.isWifiEnabled();
    }

    public void setWifiEnabled(boolean enable) {
        int wifiApState = mFlyWifiManager.getWifiApState();
        if (enable
                && (wifiApState == FlyWifiManager.WIFI_AP_STATE_ENABLED || wifiApState == FlyWifiManager.WIFI_AP_STATE_ENABLING)) {
            mFlyWifiManager.disableAP();
        }
//        if (enable) {
//            mChanger.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLING);
//        } else {
//            mChanger.onWifiStateChanged(WifiManager.WIFI_STATE_DISABLING);
//        }
        // mFragment.notifyDataSetChanged();

        if (!mFlyWifiManager.setWifiEnabled(enable)) {
            // Error
            FlyLog.e(TAG, "setWifiEnabled failed");
            if (enable) {
                mChanger.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
            } else {
                mChanger.onWifiStateChanged(WifiManager.WIFI_STATE_DISABLED);
            }
            // mFragment.notifyDataSetChanged();
            Toast.makeText(mActivity, "error", Toast.LENGTH_SHORT).show();
        }
    }

    public void setApPasswd(String passwd) {
        setApEnabled(false);
        saveApPasswd(passwd);
        setApEnabled(true);
    }

    public void setApEnabled(boolean isEnabled) {
        if (isEnabled) {
            int netStatus = SystemProperties.getInt("persist.sys.net.status", 0);
            FlyLog.d(TAG, "persist.sys.net.status=" + netStatus);
            if (netStatus == 0) {
                mFlyWifiManager.enableHotspot(loadApName(), loadApPasswd());
                if (mChanger != null) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                FlyLog.e(TAG, e);
                            }
                            if (mChanger != null) {
                                mChanger.onApEnable();
                            }
                        }
                    }).start();
                }
            } else {
                final int activeStatus = SystemProperties.getInt("persist.sys.asc.activate.status", 0);
                FlyLog.d(TAG, "persist.sys.asc.activate.status=" + activeStatus);
                if (mChanger != null) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                FlyLog.e(TAG, e);
                            }
                            if (mChanger != null) {
                                mChanger.onApStateChanged(FlyWifiManager.WIFI_AP_STATE_DISABLED);
                                mChanger.onOutOfTraffic(activeStatus);
                            }
                        }
                    }).start();
                }
            }
        } else {
            mFlyWifiManager.disableAP();
        }
    }

    private String loadApPasswd() {
        String passwd = SpUtil.getString(mActivity, "AP", "passwd", "");
        if (!TextUtils.isEmpty(passwd)) {
            return passwd;
        }
        passwd = mActivity.getString(R.string.net_ap_passwd_default);
        saveApPasswd(passwd);
        return passwd;
    }

    private void saveApPasswd(String passwd) {
        SpUtil.saveString(mActivity, "AP", "passwd", passwd);
    }

    private String loadApName() {
        String name = SpUtil.getString(mActivity, "AP", "name", "");
        if (!TextUtils.isEmpty(name)) {
            return name;
        }
        name = mActivity.getString(R.string.net_ap_name_prefix);
        name += String.format("%06d", (int) (Math.random() * 1000000));
        SpUtil.saveString(mActivity, "AP", "name", name);
        return name;
    }

    public interface IWifiViewChangedListener {
        void onWifiStateChanged(int state);

        void setWifiListItems(ArrayList<AccessPoint> accessPoints);

        ArrayList<AccessPoint> getListItems();

        void popPasswdWindow(AccessPoint bean, boolean needConnect, boolean modify);

        void updateConntecedWifiName(String name);

        void onApStateChanged(int state);

        void onApName(String name);

        void onApPasswd(String passwd);

        void onOutOfTraffic(int acticeStatus);

        void onApEnable();
    }

    class Scanner extends Handler {
        private int mRetry = 0;

        void resume() {
            if (!hasMessages(0)) {
                sendEmptyMessage(0);
            }
        }

        void forceScan() {
            removeMessages(0);
            sendEmptyMessage(0);
        }

        void pause() {
            mRetry = 0;
            removeMessages(0);
        }

        @Override
        public void handleMessage(Message message) {
            if (mFlyWifiManager.startScan()) {
                mRetry = 0;
            } else if (++mRetry >= 3) {
                mRetry = 0;
                if (mActivity != null) {
                    Toast.makeText(mActivity, "fail to scan", Toast.LENGTH_LONG)
                            .show();
                }
                return;
            }
            sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
        }
    }

}
